package com.gitee.wsl.ext.array

import com.gitee.wsl.ext.bitcode.fromLowHigh
import com.gitee.wsl.ext.bitcode.shl
import com.gitee.wsl.lang.ByteOrder

// 读取ByteArray
fun ByteArray.readByteArrayBE(offset: Int, byteLength: Int): ByteArray {
    throwOffestError(this, offset)
    return this.copyOfRange(offset, if ((offset + byteLength) > this.size) this.size else offset + byteLength)
}

fun ByteArray.readByteArrayLE(offset: Int, byteLength: Int): ByteArray {
    throwOffestError(this, offset)
    return this.readByteArrayBE(offset, byteLength).reversedArray()
}

fun ByteArray.readInt8(offset: Int = 0): Int {
    throwOffestError(this, offset, 1)
    return this[offset].toInt()
}

fun ByteArray.readUInt8(offset: Int = 0): Int {
    throwOffestError(this, offset, 1)
    return this[offset].toInt() and 0xFF
}

fun ByteArray.readInt16BE(offset: Int = 0): Int {
    throwOffestError(this, offset, 2)
    return (this[offset].toInt() shl 8) + (this[offset + 1].toInt() and 0xFF)
}

fun ByteArray.readUInt16BE(offset: Int = 0): Int {
    throwOffestError(this, offset, 2)
    // 方式一
    return ((this[offset].toInt() and 0xFF) shl 8) or (this[offset + 1].toInt() and 0xFF)
    // 方式二
//    return readUnsigned(this, 2, offset, false).toInt()
}

fun ByteArray.readInt16LE(offset: Int = 0): Int {
    throwOffestError(this, offset, 2)
    return (this[offset + 1].toInt() shl 8) + (this[offset].toInt() and 0xFF)
}

fun ByteArray.readUInt16LE(offset: Int = 0): Int {
    throwOffestError(this, offset, 2)
    return ((this[offset + 1].toInt() and 0xFF) shl 8) or (this[offset].toInt() and 0xFF)
//    return readUnsigned(this, 2, offset, true).toInt()
}

fun ByteArray.readInt32BE(offset: Int = 0): Int {
    throwOffestError(this, offset, 4)
    return (this[offset].toInt()) shl 24 or
            ((this[offset + 1].toInt() and 0xFF) shl 16) or
            ((this[offset + 2].toInt() and 0xFF) shl 8) or
            (this[offset + 3].toInt() and 0xFF)
//    return (this[offset].toInt() shl 24) + (this[offset + 1].toUByte().toInt() shl 16) + (this[offset + 2].toUByte().toInt() shl 8) + this[offset + 3].toUByte().toInt()
}

fun ByteArray.readUInt32BE(offset: Int = 0): Long {
    throwOffestError(this, offset, 4)
    return (((this[offset].toInt() and 0xFF).toLong() shl 24) or
            ((this[offset + 1].toInt() and 0xFF).toLong() shl 16) or
            ((this[offset + 2].toInt() and 0xFF).toLong() shl 8) or
            (this[offset + 3].toInt() and 0xFF).toLong())
//    return readUnsigned(this, 4, offset, false)
}

fun ByteArray.readInt32LE(offset: Int = 0): Int {
    throwOffestError(this, offset, 4)
    return (this[offset + 3].toInt()) shl 24 or
            ((this[offset + 2].toInt() and 0xFF) shl 16) or
            ((this[offset + 1].toInt() and 0xFF) shl 8) or
            (this[offset].toInt() and 0xFF)
//    return (this[offset + 3].toInt() shl 24) + (this[offset + 2].toUByte().toInt() shl 16) + (this[offset + 1].toUByte().toInt() shl 8) + this[offset].toUByte().toInt()
}

fun ByteArray.readUInt32LE(offset: Int = 0): Long {
    throwOffestError(this, offset, 4)
    return (((this[offset + 3].toInt() and 0xFF).toLong() shl 24) or
            ((this[offset + 2].toInt() and 0xFF).toLong() shl 16) or
            ((this[offset + 1].toInt() and 0xFF).toLong() shl 8) or
            (this[offset].toInt() and 0xFF).toLong())
//    return readUnsigned(this, 4, offset, true)
}

// 四字节 float
fun ByteArray.readFloatBE(offset: Int = 0) = Float.fromBits(this.readInt32BE(offset))
fun ByteArray.readFloatLE(offset: Int = 0) = Float.fromBits(this.readInt32LE(offset))

// 八字节 double
fun ByteArray.readDoubleBE(offset: Int = 0) = Double.fromBits(this.readLongBE(offset))
fun ByteArray.readDoubleLE(offset: Int = 0) = Double.fromBits(this.readLongLE(offset))

/**
 * Read [Short] in network order(BE) with specified [offset] from [ByteArray].
 */
fun ByteArray.readShortBE(offset: Int): Short {
    val result = ((this[offset].toInt() and 0xFF) shl 8) or (this[offset + 1].toInt() and 0xFF)
    return result.toShort()
}

//fun ByteArray.readLongLE(offset: Int =0 ): Long {
//    val b0 = this[offset]
//    val b1 = this[offset + 1]
//    val b2 = this[offset + 2]
//    val b3 = this[offset + 3]
//    val b4 = this[offset + 4]
//    val b5 = this[offset + 5]
//    val b6 = this[offset + 6]
//    val b7 = this[offset + 7]
//    return b0.toLong() or ((b1.toLong() and 0xff) shl 8) or ((b2.toLong() and 0xff) shl 16) or ((b3.toLong() and 0xff) shl 24) or
//            ((b4.toLong() and 0xff) shl 32) or ((b5.toLong() and 0xff) shl 40) or ((b6.toLong() and 0xff) shl 48) or ((b7.toLong() and 0xff) shl 56)
//}

fun ByteArray.readLongBE(offset: Int = 0): Long {
    val high = readInt32BE(offset)
    val low = readInt32BE(offset + 4)
    return Long.fromLowHigh(low,high)
}

fun ByteArray.readLongLE(offset: Int = 0): Long {
    val low = readInt32LE(offset)
    val high = readInt32LE(offset + 4)
    return Long.fromLowHigh(low,high)
//    return (this[offset].toLong() shl 56) or
//            (this[offset + 1].toLong() and 0xFF shl 48) or
//            (this[offset + 2].toLong() and 0xFF shl 40) or
//            (this[offset + 3].toLong() and 0xFF shl 32) or
//            (this[offset + 4].toLong() and 0xFF shl 24) or
//            (this[offset + 5].toLong() and 0xFF shl 16) or
//            (this[offset + 6].toLong() and 0xFF shl 8) or
//            (this[offset + 7].toLong() and 0xFF)
}


fun ByteArray.readByte(offset: Int = 0) = this[offset]

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.readByte(offset: Int = 0) = this[offset].toByte()


fun ByteArray.readUByte(offset: Int = 0) = this[offset].toUByte()

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.readUByte(offset: Int = 0) = this[offset]



fun ByteArray.readShort(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = readUShort(offset, byteOrder).toShort()

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.readShort(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = readUShort(offset, byteOrder).toShort()


fun ByteArray.readUShort(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = when (byteOrder) {
    ByteOrder.LittleEndian -> (readUByte(offset) + (readUByte(offset + 1).toUShort() shl 8)).toUShort()
    ByteOrder.BigEndian -> ((readUByte(offset).toUShort() shl 8) + readUByte(offset + 1)).toUShort()
}

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.readUShort(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = when (byteOrder) {
    ByteOrder.LittleEndian -> (readUByte(offset) + (readUByte(offset + 1).toUShort() shl 8)).toUShort()
    ByteOrder.BigEndian -> ((readUByte(offset).toUShort() shl 8) + readUByte(offset + 1)).toUShort()
}



fun ByteArray.readInt(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = readUInt(offset, byteOrder).toInt()

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.readInt(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = readUInt(offset, byteOrder).toInt()


fun ByteArray.readUInt(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = when (byteOrder) {
    ByteOrder.LittleEndian -> readUShort(offset) + (readUShort(offset + 2).toUInt() shl 16)
    ByteOrder.BigEndian -> (readUShort(offset).toUInt() shl 16) + readUShort(offset + 2)
}

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.readUInt(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = when (byteOrder) {
    ByteOrder.LittleEndian -> readUShort(offset) + (readUShort(offset + 2).toUInt() shl 16)
    ByteOrder.BigEndian -> (readUShort(offset).toUInt() shl 16) + readUShort(offset + 2)
}


fun ByteArray.readLong(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = readULong(offset, byteOrder).toLong()

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.readLong(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = readULong(offset, byteOrder).toLong()


fun ByteArray.readULong(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = when (byteOrder) {
    ByteOrder.LittleEndian -> readUInt(offset) + (readUInt(offset + 4).toULong() shl 32)
    ByteOrder.BigEndian -> (readUInt(offset).toULong() shl 32) + readUInt(offset + 4)
}

@OptIn(ExperimentalUnsignedTypes::class)
fun UByteArray.readULong(offset: Int = 0, byteOrder: ByteOrder = ByteOrder.LittleEndian) = when (byteOrder) {
    ByteOrder.LittleEndian -> readUInt(offset) + (readUInt(offset + 4).toULong() shl 32)
    ByteOrder.BigEndian -> (readUInt(offset).toULong() shl 32) + readUInt(offset + 4)
}